package at.fpmedv.eclipse.helma.console;

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IExecutableExtensionFactory;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.ui.console.IOConsole;
import org.eclipse.ui.console.IOConsoleOutputStream;

import at.fpmedv.eclipse.helma.core.HelmaCorePlugin;
import at.fpmedv.eclipse.helma.core.HelmaServer;
import at.fpmedv.eclipse.helma.logging.IEclipseHelmaLogger;

/**
 * We just need to extend the IOConsole for our logging
 * 
 * @author fpm
 *
 */
public class HelmaLoggingConsole extends IOConsole implements IEclipseHelmaLogger, IExecutableExtensionFactory  {

    public final static String CONSOLE_TYPE = "helmaConsoleLog"; //$NON-NLS-1$

    /**
     * default Format for dates
     */
    static DateFormat dformat = new SimpleDateFormat("[yyyy/MM/dd HH:mm:ss] ");
    
    /**
     * storage for last pregenerated Date
     */
    static long dateLastRendered;
    
    /**
     * DateCache is filled every second for performance reasons
     */
    static String dateCache;
    
    /**
     * int constant for TRACE loglevel
     */
    public final static int TRACE = 1;

    /**
     * int constant for DEBUG loglevel
     */
    public final static int DEBUG = 2;

    /**
     * int constant for INFO loglevel
     */
    public final static int INFO =  3;

    /**
     * int constant for WARN loglevel
     */
    public final static int WARN =  4;

    /**
     * int constant for ERROR loglevel
     */
    public final static int ERROR = 5;

    /**
     * int constant for FATAL loglevel
     */
    public final static int FATAL = 6;
    
    /**
     * default logLevel
     */
    int logLevel = INFO;
    
	private Color messageColor;
	private Color errorColor;


	private IOConsoleOutputStream messageStream;
	private IOConsoleOutputStream errorStream;

	private volatile static HelmaLoggingConsole singelton;
	
	/**
	 * just a fake for the createExecutableExtension
	 * 
	 */
	public HelmaLoggingConsole() {
		super("HelmaFactory", null);
	}
	
	/**
	 * just a fake
	 * 
	 * @param name the human readable name of the logger to create
	 * @param imageDescriptor icon to use for view
	 */
	private HelmaLoggingConsole(String name, ImageDescriptor imageDescriptor) {
		super(name, HelmaLoggingConsole.CONSOLE_TYPE, imageDescriptor);
	}

	/**
	 * sets loglevel from helmaServerProperties
	 */
	public void initVars() {
		if (HelmaServer.helmaServerProperties != null) {
			String logLevel = HelmaServer.helmaServerProperties.getProperty("loglevel");
	        if ("trace".equalsIgnoreCase(logLevel))
	            this.logLevel = TRACE;
	        else if ("debug".equalsIgnoreCase(logLevel))
	        	this.logLevel = DEBUG;
	        else if ("info".equalsIgnoreCase(logLevel))
	        	this.logLevel = INFO;
	        else if ("warn".equalsIgnoreCase(logLevel))
	        	this.logLevel = WARN;
	        else if ("error".equalsIgnoreCase(logLevel))
	        	this.logLevel = ERROR;
	        else if ("fatal".equalsIgnoreCase(logLevel))
	        	this.logLevel = FATAL;
		}
		
		// create streams
		messageStream = newOutputStream();
		errorStream = newOutputStream();
		
		// set colors
		messageColor = new Color(HelmaCorePlugin.getStandardDisplay(), new RGB(0, 0, 0));
		messageStream.setColor(messageColor);

		errorColor = new Color(HelmaCorePlugin.getStandardDisplay(), new RGB(255, 0, 0));
		errorStream.setColor(errorColor);
		try {
			messageStream.write("Logging started\n");
		} catch (IOException e) {
			HelmaCorePlugin.getDefault().log("error writing to stream", e);
		}
		
	}
	/**
	 * pass through to {@link #println(Object, Throwable)}
	 */
	public void debug(Object msg) {
        if (logLevel <= DEBUG)
        	println("debug", msg, null);
	}

	/**
	 * pass through to {@link #println(Object, Throwable)}
	 */
	public void debug(Object msg, Throwable t) {
        if (logLevel <= DEBUG)
        	println("debug", msg, t);
	}

	/**
	 * pass through to {@link #println(Object, Throwable)}
	 */
	public void error(Object msg) {
        if (logLevel <= ERROR)
        	println("error", msg, null);
	}

	/**
	 * pass through to {@link #println(Object, Throwable)}
	 */
	public void error(Object msg, Throwable t) {
        if (logLevel <= ERROR)
        	println("error", msg, t);
	}

	/**
	 * pass through to {@link #println(Object, Throwable)}
	 */
	public void fatal(Object msg) {
        if (logLevel <= FATAL)
        	println("fatal", msg, null);
	}

	/**
	 * pass through to {@link #println(Object, Throwable)}
	 */
	public void fatal(Object msg, Throwable t) {
        if (logLevel <= FATAL)
        	println("fatal", msg, t);
	}

	/**
	 * pass through to {@link #println(Object, Throwable)}
	 */
	public void info(Object msg) {
        if (logLevel <= INFO)
        	println("info", msg, null);
	}

	/**
	 * pass through to {@link #println(Object, Throwable)}
	 */
	public void info(Object msg, Throwable t) {
        if (logLevel <= INFO)
        	println("info", msg, t);
	}

	/**
	 * pass through to {@link #println(Object, Throwable)}
	 */
	public void trace(Object msg, Throwable t) {
        if (logLevel <= TRACE)
        	println("trace", msg, null);
	}

	/**
	 * pass through to {@link #println(Object, Throwable)}
	 */
	public void trace(Object msg) {
        if (logLevel <= TRACE)
        	println("trace", msg, null);
	}

	/**
	 * pass through to {@link #println(Object, Throwable)}
	 */
	public void warn(Object msg) {
        if (logLevel <= WARN)
        	println("warn", msg, null);
	}

	/**
	 * pass through to {@link #println(Object, Throwable)}
	 */
	public void warn(Object msg, Throwable t) {
        if (logLevel <= WARN)
        	println("warn", msg, t);
	}
	
	/**
	 * prints the passed params to the configured Printer
	 * 
	 * @param msg Some message text
	 * @param t a java Throwable
	 */
	private void println(String prefix, Object msg, Throwable t) {
        // it's enough to render the date every second
        if ((System.currentTimeMillis() - 1000) > dateLastRendered) {
        	preGenerateDate();
        }
        Thread thread = Thread.currentThread();
        String threadId = "[" + thread.getName() + "] ";
		String line = dateCache + threadId;
		line += prefix != null ? "[" + prefix + "] " + msg : msg;
		if (t != null) {
			line += " " + t;
		}
		boolean isError = (prefix == "error" || prefix == "fatal" || prefix == "warn");
		try {
			if(isError)
				errorStream.write(line + "\n");
			else
				messageStream.write(line + "\n");
		} catch (IOException e) {
			if (HelmaCorePlugin.getDefault() != null)
				HelmaCorePlugin.getDefault().log("println: error writing to stream", e);
		}
	}
	
	/**
	 *  Pre-generates the date String to use for log messages. Called about once a second.
	 */
    protected static synchronized void preGenerateDate() {
        Date date = new Date();
        dateCache = dformat.format(date);
        dateLastRendered = date.getTime();
    }

	@Override
	protected void dispose() {
		super.dispose();
		if (messageColor != null)
			messageColor.dispose();
		if (errorColor != null)
			errorColor.dispose();
	}

	public static synchronized HelmaLoggingConsole getInstance() {
		if (singelton == null) {
			singelton = new HelmaLoggingConsole("Helma", null);
			singelton.initVars();
		}
		return singelton;
	}

	public Object create() throws CoreException {
		return HelmaLoggingConsole.getInstance();
	}
}
